﻿using System;
using System.Linq.Expressions;
using System.Collections.Generic;
using Caramel;

namespace Caramel.Entities.Testing
{
	public static class EntitySpecificationExtensions
	{
		public static EntitySpecification<T> CheckProperty<T>(
			this EntitySpecification<T> specification,
			Expression<Func<T, object>> expression, 
			object propertyValue) where T : class, IEntity, new()
		{
			return 
				specification.
					RegisterCheckedProperty(
						new SimpleProperty<T, object>(expression.GetProperty(), (x, y) => x.Equals(y))
						{
							ValueFactory = e => propertyValue
						}
					);
		}

		public static EntitySpecification<T> CheckProperty<T, TProperty>(
			this EntitySpecification<T> specification, 
			Expression<Func<T, TProperty>> expression, TProperty propertyValue, 
			IEqualityComparer<TProperty> comparer) where T : class, IEntity, new()
		{
			return
				specification.
					RegisterCheckedProperty(
						new SimpleProperty<T, TProperty>(expression.GetProperty(), (x, y) => comparer.Equals((TProperty)x, (TProperty)y))
						{
							ValueFactory = e => propertyValue
						}
					);
		}

		public static EntitySpecification<T> CheckReference<T, TReference>(
			this EntitySpecification<T> specification, 
			Expression<Func<T, object>> expression, TReference propertyValue) where T : class, IEntity, new()
		{
			return
				specification.
					RegisterCheckedProperty(
						new ReferenceProperty<T, TReference>(expression.GetProperty(), (x, y) => x.Equals(y))
						{
							ValueFactory = e => propertyValue
						}
					);
		}

		public static EntitySpecification<T> CheckReference<T, TReference>(
			this EntitySpecification<T> specification,
			Expression<Func<T, object>> expression, TReference propertyValue,
			IEqualityComparer<TReference> comparer) where T : class, IEntity, new()
		{
			return
				specification.
					RegisterCheckedProperty(
						new ReferenceProperty<T, TReference>(expression.GetProperty(), (x, y) => comparer.Equals((TReference)x, (TReference)y))
						{
							ValueFactory = e => propertyValue
						}
					);
		}

		public static EntitySpecification<T> CheckList<T, TListEmelent>(
			this EntitySpecification<T> specification, 
			Expression<Func<T, IEnumerable<TListEmelent>>> expression, 
			Func<T, IEnumerable<TListEmelent>> propertyValueFactory) where T : class, IEntity, new()
		{
			return
				specification.
					RegisterCheckedProperty(
						new ReferenceListProperty<T, TListEmelent>(expression.GetProperty(), (x, y) => x.Equals(y))
						{
							ValueFactory = e => propertyValueFactory(e)
						}
					);
		}

		public static EntitySpecification<T> CheckList<T, TListEmelent>(
			this EntitySpecification<T> specification,
			Expression<Func<T, IEnumerable<TListEmelent>>> expression,
			Func<T, IEnumerable<TListEmelent>> propertyValueFactory,
			IEqualityComparer<IEnumerable<TListEmelent>> comparer) where T : class, IEntity, new()
		{
			return
				specification.
					RegisterCheckedProperty(
						new ReferenceListProperty<T, TListEmelent>(expression.GetProperty(), (x, y) => comparer.Equals((IEnumerable<TListEmelent>)x, (IEnumerable<TListEmelent>)y))
						{
							ValueFactory = e => propertyValueFactory(e)
						}
					);
		}

		public static EntitySpecification<T> CheckList<T, TListEmelent>(
			this EntitySpecification<T> specification, 
			Expression<Func<T, IEnumerable<TListEmelent>>> expression, 
			IEnumerable<TListEmelent> propertyValue) where T : class, IEntity, new()
		{
			return
				specification.
					RegisterCheckedProperty(
						new ReferenceListProperty<T, TListEmelent>(expression.GetProperty(), (x, y) => x.Equals(y))
						{
							ValueFactory = e => propertyValue
						}
					);
		}

		public static EntitySpecification<T> CheckList<T, TListEmelent>(
			this EntitySpecification<T> specification,
			Expression<Func<T, IEnumerable<TListEmelent>>> expression,
			IEnumerable<TListEmelent> propertyValue,
			IEqualityComparer<IEnumerable<TListEmelent>> comparer) where T : class, IEntity, new()
		{
			return
				specification.
					RegisterCheckedProperty(
						new ReferenceListProperty<T, TListEmelent>(expression.GetProperty(), (x, y) => comparer.Equals((IEnumerable<TListEmelent>)x, (IEnumerable<TListEmelent>)y))
						{
							ValueFactory = e => propertyValue
						}
					);
		}
	}
}
